package SharedRAMArbiter ( mkSharedRAMArbiter, RAM ) where

import Vector
import FIFO


interface RAM a b =
    send :: a -> Action
    receive :: ActionValue b


mkSharedRAMArbiter :: (Log n k) => RAM a b -> Module (Vector n (RAM a b))
mkSharedRAMArbiter ram = do
    tagFifo :: FIFO (Bit k)
    tagFifo <- mkFIFO

    let mkSharedRam :: Integer -> Module (RAM a b)
        mkSharedRam i = return $
            interface RAM
                send x = action { tagFifo.enq (fromInteger i);
                           ram.send x; }
                receive = do
                            tagFifo.deq
                            ram.receive
                          when (tagFifo.first == fromInteger i)

    mapM mkSharedRam genList


----

type Word = Bit 16

mkArbiterTest :: Module Empty
mkArbiterTest =
    module
        sram :: RAM Word Word
        sram <- mkRAM

        rams :: Vector 3 (RAM Word Word)
        rams <- mkSharedRAMArbiter sram

        mkContender 2 "1" (rams!!0)
        mkContender 3 "2" (rams!!1)
        mkContender 5 "3" (rams!!2)

mkContender :: Word -> String -> RAM Word Word -> Module Empty
mkContender initial_request str ram =
    module
        request :: Reg (Maybe Word)
        request <- mkReg (Valid initial_request)

        counter :: Reg Word
        counter <- mkReg 0

        counter_step :: Reg Word
        counter_step <- mkReg 1

        counter_last :: Reg Word
        counter_last <- mkReg 1

        let name = "User" +++ str
        rules
          (name +++ "_Count"):
            when True
              ==> counter := counter + 1

          (name +++ "_Request"):
            when (counter >= counter_last), Valid x <- request
              ==> action
                    counter_step := counter_step + 1
                    counter_last := counter + counter_step
                    ram.send x
                    request := Invalid

          (name +++ "_Receive"):
            when True
              ==> action
                     res :: Word
                     res <- ram.receive
                     request := Valid res


mkRAM :: (Bits n s, Arith n) => Module (RAM n n)
mkRAM =
    module
        incoming :: FIFO n
        incoming <- mkFIFO

        outgoing :: FIFO n
        outgoing <- mkFIFO
        interface
            send x = incoming.enq x
            receive = do
                        action outgoing.deq
                        return (outgoing.first * 2)
        rules
            -- simulate latency?
            when True
              ==> action
                    outgoing.enq incoming.first
                    incoming.deq
